// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件提供内部调试日志记录功能。debug 
// 日志是一个轻量级的内存中的每米环形缓冲区。默认情况下，
// 运行时会在死机时打印调试日志。
// 
// 要将某些内容打印到调试日志中，请调用dlog以获取一个dloger 
// 并使用其上的方法添加值。这些值将是
// 在输出中用空格分隔（与println非常相似）。
// 
// 当
// 构建时，可以通过传递-tags debuglog来启用此功能。如果没有这个标记，dlog调用compile将一无所获。

package runtime

import (
	"runtime/internal/atomic"
	"unsafe"
)

// debugLogBytes是每个每米环形缓冲区的大小。这是从堆外分配的
// 以避免破坏M，从而导致GC的
// 堆大小。
const debugLogBytes = 16 << 10

// debugLogStringLimit是字符串中的最大字节数。
// 在上面，字符串将被截断为“。（n个字节）…”
const debugLogStringLimit = debugLogBytes / 8

// dlog返回调试记录器。调用方可以使用返回的记录器上的方法来添加值，这些值将在最终输出中用空格分隔，与println非常相似。调用者必须调用end（）来完成消息。
// 
// dlog可以在运行时高度受限的角落使用：它可以安全地在信号处理程序中使用，在写
// 屏障中使用，在堆栈实现中使用，在
// 必须递归无分裂的地方使用。
// 
// 如果在没有debuglog build标记的情况下构建，则会编译掉该文件。
// 然而，论点结构可能不正确。如果任何参数abcDef
// 
// go:nosplit 
// go:nowritebarrierrec 
func dlog() *dlogger {
	if !dlogEnabled {
		return nil
	}

	// 获取时间。
	tick, nano := uint64(cputicks()), uint64(nanotime())

	// 尝试获取缓存的记录器。
	l := getCachedDlogger()

	// 如果我们无法获取缓存的记录器，请尝试从
	// 全局池获取一个。
	if l == nil {
		allp := (*uintptr)(unsafe.Pointer(&allDloggers))
		all := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr(allp)))
		for l1 := all; l1 != nil; l1 = l1.allLink {
			if atomic.Load(&l1.owned) == 0 && atomic.Cas(&l1.owned, 0, 1) {
				l = l1
				break
			}
		}
	}

	// 如果失败，请分配一个新的记录器。
	if l == nil {
		l = (*dlogger)(sysAlloc(unsafe.Sizeof(dlogger{}), nil))
		if l == nil {
			throw("failed to allocate debug log")
		}
		l.w.r.data = &l.w.data
		l.owned = 1

		// 在AlldLogger列表前加上前缀。
		headp := (*uintptr)(unsafe.Pointer(&allDloggers))
		for {
			head := atomic.Loaduintptr(headp)
			l.allLink = (*dlogger)(unsafe.Pointer(head))
			if atomic.Casuintptr(headp, head, uintptr(unsafe.Pointer(l))) {
				break
			}
		}
	}

	// 如果时间差太大，请编写一个新的同步
	// 数据包。我们设置了限制，所以在记录头中写入的增量不会超过6个
	// 字节。
	const deltaLimit = 1<<(3*7) - 1 // /~2ms在同步数据包之间
	if tick-l.w.tick > deltaLimit || nano-l.w.nano > deltaLimit {
		l.w.writeSync(tick, nano)
	}

	// 为帧头保留空间。
	l.w.ensure(debugLogHeaderSize)
	l.w.write += debugLogHeaderSize

	// 写入记录头。
	l.w.uvarint(tick - l.w.tick)
	l.w.uvarint(nano - l.w.nano)
	gp := getg()
	if gp != nil && gp.m != nil && gp.m.p != 0 {
		l.w.varint(int64(gp.m.p.ptr().id))
	} else {
		l.w.varint(-1)
	}

	return l
}

// 数据记录器写入调试日志。
// 
// 要获得一个dloger，请调用dlog（）。使用dlogger后，调用
// end（）。
// 
// go:notinheap 
type dlogger struct {
	w debugLogWriter

	// allLink是AlldLogger列表中的下一个日志记录器。
	allLink *dlogger

	// owned表示该数据记录器归M所有。这是
	// /以原子方式访问。
	owned uint32
}

// AlldLogger是所有dlogger的列表，通过
// dlogger链接。allLink。这是原子访问的。这只是预编，
// 所以它不需要防止ABA种族。
var allDloggers *dlogger

// go:nosplit 
func (l *dlogger) end() {
	if !dlogEnabled {
		return
	}

	// 填写帧头。
	size := l.w.write - l.w.r.end
	if !l.w.writeFrameAt(l.w.r.end, size) {
		throw("record too large")
	}

	// 提交记录。
	l.w.r.end = l.w.write

	// 尝试将此记录器返回到缓存。
	if putCachedDlogger(l) {
		return
	}

	// 将记录器返回到全局池。目前，中国政府的以下几几点：
	atomic.Store(&l.owned, 0)
}

const (
	debugLogUnknown = 1 + iota
	debugLogBoolTrue
	debugLogBoolFalse
	debugLogInt
	debugLogUint
	debugLogHex
	debugLogPtr
	debugLogString
	debugLogConstString
	debugLogStringOverflow

	debugLogPC
	debugLogTraceback
)

func (l *dlogger) b(x bool) *dlogger {
	if !dlogEnabled {
		return l
	}
	if x {
		l.w.byte(debugLogBoolTrue)
	} else {
		l.w.byte(debugLogBoolFalse)
	}
	return l
}

func (l *dlogger) i(x int) *dlogger {
	return l.i64(int64(x))
}

func (l *dlogger) i8(x int8) *dlogger {
	return l.i64(int64(x))
}

func (l *dlogger) i16(x int16) *dlogger {
	return l.i64(int64(x))
}

func (l *dlogger) i32(x int32) *dlogger {
	return l.i64(int64(x))
}

func (l *dlogger) i64(x int64) *dlogger {
	if !dlogEnabled {
		return l
	}
	l.w.byte(debugLogInt)
	l.w.varint(x)
	return l
}

func (l *dlogger) u(x uint) *dlogger {
	return l.u64(uint64(x))
}

func (l *dlogger) uptr(x uintptr) *dlogger {
	return l.u64(uint64(x))
}

func (l *dlogger) u8(x uint8) *dlogger {
	return l.u64(uint64(x))
}

func (l *dlogger) u16(x uint16) *dlogger {
	return l.u64(uint64(x))
}

func (l *dlogger) u32(x uint32) *dlogger {
	return l.u64(uint64(x))
}

func (l *dlogger) u64(x uint64) *dlogger {
	if !dlogEnabled {
		return l
	}
	l.w.byte(debugLogUint)
	l.w.uvarint(x)
	return l
}

func (l *dlogger) hex(x uint64) *dlogger {
	if !dlogEnabled {
		return l
	}
	l.w.byte(debugLogHex)
	l.w.uvarint(x)
	return l
}

func (l *dlogger) p(x any) *dlogger {
	if !dlogEnabled {
		return l
	}
	l.w.byte(debugLogPtr)
	if x == nil {
		l.w.uvarint(0)
	} else {
		v := efaceOf(&x)
		switch v._type.kind & kindMask {
		case kindChan, kindFunc, kindMap, kindPtr, kindUnsafePointer:
			l.w.uvarint(uint64(uintptr(v.data)))
		default:
			throw("not a pointer type")
		}
	}
	return l
}

// go:nosplit 
func (l *dlogger) s(x string) *dlogger {
	if !dlogEnabled {
		return l
	}
	str := stringStructOf(&x)
	datap := &firstmoduledata
	if len(x) > 4 && datap.etext <= uintptr(str.str) && uintptr(str.str) < datap.end {
		// 字符串常量位于rodata部分，而
		// 未记录在moduledata中。但它必须是
		// 介于etext和end之间。debugLogWriter是二进制调试日志记录的环形缓冲区。
		l.w.byte(debugLogConstString)
		l.w.uvarint(uint64(str.len))
		l.w.uvarint(uint64(uintptr(str.str) - datap.etext))
	} else {
		l.w.byte(debugLogString)
		var b []byte
		bb := (*slice)(unsafe.Pointer(&b))
		bb.array = str.str
		bb.len, bb.cap = str.len, str.len
		if len(b) > debugLogStringLimit {
			b = b[:debugLogStringLimit]
		}
		l.w.uvarint(uint64(len(b)))
		l.w.bytes(b)
		if len(b) != len(x) {
			l.w.byte(debugLogStringOverflow)
			l.w.uvarint(uint64(len(x) - len(b)))
		}
	}
	return l
}

func (l *dlogger) pc(x uintptr) *dlogger {
	if !dlogEnabled {
		return l
	}
	l.w.byte(debugLogPC)
	l.w.uvarint(uint64(x))
	return l
}

func (l *dlogger) traceback(x []uintptr) *dlogger {
	if !dlogEnabled {
		return l
	}
	l.w.byte(debugLogTraceback)
	l.w.uvarint(uint64(len(x)))
	for _, pc := range x {
		l.w.uvarint(uint64(pc))
	}
	return l
}

// 
// 日志记录由一个2字节的帧头和一系列
// 字段组成。帧头将记录的大小表示为一个小的
// endian 16位值。每个字段都以一个字节开头，表示其
// 类型，后跟特定于类型的数据。如果帧
// 头中的大小为0，则它是由两个小的尾端
// 64位值提供了一个新的时基。因为这是一个环形缓冲区，新记录最终将覆盖旧记录。因此，它维护了一个读卡器，当日志被覆盖时，它会使用
// 日志。读卡器状态是
// 实际日志读卡器的启动位置。
// 
// go:notinheap 
type debugLogWriter struct {
	write uint64
	data  debugLogBuf

	// tick和nano是最新
	// 书面同步记录的时基。
	tick, nano uint64

	// r是一个读卡器，当记录被写入程序覆盖时，它会使用这些记录。打印日志时，它还充当初始读卡器状态
	// 。
	r debugLogReader

	// buf是用于编码的暂存缓冲区。这是为了减少堆栈的使用。
	buf [10]byte
}

// go:notinheap 
type debugLogBuf [debugLogBytes]byte

const (
	// debugLogHeaderSize是每个dlog记录的帧
	// 头中的字节数。
	debugLogHeaderSize = 2

	// debugLogSyncSize是同步记录中的字节数。
	debugLogSyncSize = debugLogHeaderSize + 2*8
)

// go:nosplit 
func (l *debugLogWriter) ensure(n uint64) {
	for l.write+n >= l.r.begin+uint64(len(l.data)) {
		// 开始时使用记录。
		if l.r.skip() == ^uint64(0) {
			// 在一个记录中。
			// 
			// TODO（奥斯汀）：此时最好是吃掉整个缓冲区，但我们必须以某种方式与读者沟通。go:nosplit-
			throw("record wrapped around")
		}
	}
}

func (l *debugLogWriter) writeFrameAt(pos, size uint64) bool {
	l.data[pos%uint64(len(l.data))] = uint8(size)
	l.data[(pos+1)%uint64(len(l.data))] = uint8(size >> 8)
	return size <= 0xFFFF
}

func (l *debugLogWriter) writeSync(tick, nano uint64) {
	l.tick, l.nano = tick, nano
	l.ensure(debugLogHeaderSize)
	l.writeFrameAt(l.write, 0)
	l.write += debugLogHeaderSize
	l.writeUint64LE(tick)
	l.writeUint64LE(nano)
	l.r.end = l.write
}

// go:nosplit-
func (l *debugLogWriter) writeUint64LE(x uint64) {
	var b [8]byte
	b[0] = byte(x)
	b[1] = byte(x >> 8)
	b[2] = byte(x >> 16)
	b[3] = byte(x >> 24)
	b[4] = byte(x >> 32)
	b[5] = byte(x >> 40)
	b[6] = byte(x >> 48)
	b[7] = byte(x >> 56)
	l.bytes(b[:])
}

// go:nosplit-
func (l *debugLogWriter) byte(x byte) {
	l.ensure(1)
	pos := l.write
	l.write++
	l.data[pos%uint64(len(l.data))] = x
}

// go:nosplit-
func (l *debugLogWriter) bytes(x []byte) {
	l.ensure(uint64(len(x)))
	pos := l.write
	l.write += uint64(len(x))
	for len(x) > 0 {
		n := copy(l.data[pos%uint64(len(l.data)):], x)
		pos += uint64(n)
		x = x[n:]
	}
}

// go:nosplit-
func (l *debugLogWriter) varint(x int64) {
	var u uint64
	if x < 0 {
		u = (^uint64(x) << 1) | 1 // 补码i，0位是1-
	} else {
		u = (uint64(x) << 1) // 不补码i，位0是0 
	}
	l.uvarint(u)
}

// go:nosplit 
func (l *debugLogWriter) uvarint(u uint64) {
	i := 0
	for u >= 0x80 {
		l.buf[i] = byte(u) | 0x80
		u >>= 7
		i++
	}
	l.buf[i] = byte(u)
	i++
	l.bytes(l.buf[:i])
}

type debugLogReader struct {
	data *debugLogBuf

	// 开始和结束是开始
	// 和结束日志数据的位置，单位为len（数据）。
	begin, end uint64

	// tick和nano是开始时的当前时基。
	tick, nano uint64
}

// go:nosplit 
func (r *debugLogReader) skip() uint64 {
	// 读取位置的大小
	if r.begin+debugLogHeaderSize > r.end {
		return ^uint64(0)
	}
	size := uint64(r.readUint16LEAt(r.begin))
	if size == 0 {
		// 同步数据包。
		r.tick = r.readUint64LEAt(r.begin + debugLogHeaderSize)
		r.nano = r.readUint64LEAt(r.begin + debugLogHeaderSize + 8)
		size = debugLogSyncSize
	}
	if r.begin+size > r.end {
		return ^uint64(0)
	}
	r.begin += size
	return size
}

// go:nosplit 
func (r *debugLogReader) readUint16LEAt(pos uint64) uint16 {
	return uint16(r.data[pos%uint64(len(r.data))]) |
		uint16(r.data[(pos+1)%uint64(len(r.data))])<<8
}

// go:nosplit 
func (r *debugLogReader) readUint64LEAt(pos uint64) uint64 {
	var b [8]byte
	for i := range b {
		b[i] = r.data[pos%uint64(len(r.data))]
		pos++
	}
	return uint64(b[0]) | uint64(b[1])<<8 |
		uint64(b[2])<<16 | uint64(b[3])<<24 |
		uint64(b[4])<<32 | uint64(b[5])<<40 |
		uint64(b[6])<<48 | uint64(b[7])<<56
}

func (r *debugLogReader) peek() (tick uint64) {
	// 使用任何同步记录。
	size := uint64(0)
	for size == 0 {
		if r.begin+debugLogHeaderSize > r.end {
			return ^uint64(0)
		}
		size = uint64(r.readUint16LEAt(r.begin))
		if size != 0 {
			break
		}
		if r.begin+debugLogSyncSize > r.end {
			return ^uint64(0)
		}
		// 同步数据包。
		r.tick = r.readUint64LEAt(r.begin + debugLogHeaderSize)
		r.nano = r.readUint64LEAt(r.begin + debugLogHeaderSize + 8)
		r.begin += debugLogSyncSize
	}

	// Peek tick delta。
	if r.begin+size > r.end {
		return ^uint64(0)
	}
	pos := r.begin + debugLogHeaderSize
	var u uint64
	for i := uint(0); ; i += 7 {
		b := r.data[pos%uint64(len(r.data))]
		pos++
		u |= uint64(b&^0x80) << i
		if b&0x80 == 0 {
			break
		}
	}
	if pos > r.begin+size {
		return ^uint64(0)
	}
	return r.tick + u
}

func (r *debugLogReader) header() (end, tick, nano uint64, p int) {
	// 读取大小。我们已经跳过了同步数据包，并在peek中检查了
	// 边界。
	size := uint64(r.readUint16LEAt(r.begin))
	end = r.begin + size
	r.begin += debugLogHeaderSize

	// 读取tick、nano和p。
	tick = r.uvarint() + r.tick
	nano = r.uvarint() + r.nano
	p = int(r.varint())

	return
}

func (r *debugLogReader) uvarint() uint64 {
	var u uint64
	for i := uint(0); ; i += 7 {
		b := r.data[r.begin%uint64(len(r.data))]
		r.begin++
		u |= uint64(b&^0x80) << i
		if b&0x80 == 0 {
			break
		}
	}
	return u
}

func (r *debugLogReader) varint() int64 {
	u := r.uvarint()
	var v int64
	if u&1 == 0 {
		v = int64(u >> 1)
	} else {
		v = ^int64(u >> 1)
	}
	return v
}

func (r *debugLogReader) printVal() bool {
	typ := r.data[r.begin%uint64(len(r.data))]
	r.begin++

	switch typ {
	default:
		print("<unknown field type ", hex(typ), " pos ", r.begin-1, " end ", r.end, ">\n")
		return false

	case debugLogUnknown:
		print("<unknown kind>")

	case debugLogBoolTrue:
		print(true)

	case debugLogBoolFalse:
		print(false)

	case debugLogInt:
		print(r.varint())

	case debugLogUint:
		print(r.uvarint())

	case debugLogHex, debugLogPtr:
		print(hex(r.uvarint()))

	case debugLogString:
		sl := r.uvarint()
		if r.begin+sl > r.end {
			r.begin = r.end
			print("<string length corrupted>")
			break
		}
		for sl > 0 {
			b := r.data[r.begin%uint64(len(r.data)):]
			if uint64(len(b)) > sl {
				b = b[:sl]
			}
			r.begin += uint64(len(b))
			sl -= uint64(len(b))
			gwrite(b)
		}

	case debugLogConstString:
		len, ptr := int(r.uvarint()), uintptr(r.uvarint())
		ptr += firstmoduledata.etext
		str := stringStruct{
			str: unsafe.Pointer(ptr),
			len: len,
		}
		s := *(*string)(unsafe.Pointer(&str))
		print(s)

	case debugLogStringOverflow:
		print("..(", r.uvarint(), " more bytes)..")

	case debugLogPC:
		printDebugLogPC(uintptr(r.uvarint()), false)

	case debugLogTraceback:
		n := int(r.uvarint())
		for i := 0; i < n; i++ {
			print("\n\t")
			// gentraceback PC始终是返回PC。
			// 将其转换为呼叫PC。
			// 
			// TODO（奥斯汀）：展开内联帧。
			printDebugLogPC(uintptr(r.uvarint()), true)
		}
	}

	return true
}

// printDebugLog打印调试日志。
func printDebugLog() {
	if !dlogEnabled {
		return
	}

	// 此函数不应死机或抛出，因为它用于
	// 致命死机路径，这可能会死机。

	printlock()

	// 获取所有调试日志的列表。
	allp := (*uintptr)(unsafe.Pointer(&allDloggers))
	all := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr(allp)))

	// 清点日志。
	n := 0
	for l := all; l != nil; l = l.allLink {
		n++
	}
	if n == 0 {
		printunlock()
		return
	}

	// 为所有日志准备读取状态。
	type readState struct {
		debugLogReader
		first    bool
		lost     uint64
		nextTick uint64
	}
	state1 := sysAlloc(unsafe.Sizeof(readState{})*uintptr(n), nil)
	if state1 == nil {
		println("failed to allocate read state for", n, "logs")
		printunlock()
		return
	}
	state := (*[1 << 20]readState)(state1)[:n]
	{
		l := all
		for i := range state {
			s := &state[i]
			s.debugLogReader = l.w.r
			s.first = true
			s.lost = l.w.r.begin
			s.nextTick = s.peek()
			l = l.allLink
		}
	}

	// 打印记录。
	for {
		// 查找下一条记录。
		var best struct {
			tick uint64
			i    int
		}
		best.tick = ^uint64(0)
		for i := range state {
			if state[i].nextTick < best.tick {
				best.tick = state[i].nextTick
				best.i = i
			}
		}
		if best.tick == ^uint64(0) {
			break
		}

		// 打印记录。
		s := &state[best.i]
		if s.first {
			print(">> begin log ", best.i)
			if s.lost != 0 {
				print("; lost first ", s.lost>>10, "KB")
			}
			print(" <<\n")
			s.first = false
		}

		end, _, nano, p := s.header()
		oldEnd := s.end
		s.end = end

		print("[")
		var tmpbuf [21]byte
		pnano := int64(nano) - runtimeInitTime
		if pnano < 0 {
			// 在设置runtimeInitTime之前记录。
			pnano = 0
		}
		print(string(itoaDiv(tmpbuf[:], uint64(pnano), 9)))
		print(" P ", p, "] ")

		for i := 0; s.begin < s.end; i++ {
			if i > 0 {
				print(" ")
			}
			if !s.printVal() {
				// 中止此P日志。
				print("<aborting P log>")
				end = oldEnd
				break
			}
		}
		println()

		// 继续下一条记录。
		s.begin = end
		s.end = oldEnd
		s.nextTick = s.peek()
	}

	printunlock()
}

// PrintDebuggLogPC打印单个符号化PC。如果returnPC为true，则
// PC是必须首先转换为呼叫PC的返回PC。
func printDebugLogPC(pc uintptr, returnPC bool) {
	fn := findfunc(pc)
	if returnPC && (!fn.valid() || pc > fn.entry()) {
		// TODO（austin）：如果前一帧
		// 是信号，则不要备份。
		pc--
	}

	print(hex(pc))
	if !fn.valid() {
		print(" [unknown PC]")
	} else {
		name := funcname(fn)
		file, line := funcline(fn, pc)
		print(" [", name, "+", hex(pc-fn.entry()),
			" ", file, ":", line, "]")
	}
}
